package hku.cs.lunuo.rank;

import hku.cs.lunuo.Global;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import soot.Body;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.jimple.ReturnStmt;
import soot.jimple.ReturnVoidStmt;
import soot.options.Options;
import soot.toolkits.graph.Block;
import soot.toolkits.graph.BlockGraph;
import soot.toolkits.graph.ExceptionalBlockGraph;

public class KBCModel_WithSwitchReturn extends EsBasingModel {

	@Override
	public void analyze() {
		for (String klassName : esStatistic.getKlassNames()) {
			for (String methodName : esStatistic.getMethodNames(klassName)) {
				for (String es : esStatistic.getEss(klassName, methodName)) {
					StatisticProperty sp = esStatistic.getProperty(klassName,
							methodName, es);

					VotedUnit u = new VotedUnit(new EsUnit(klassName,
							methodName, es), sp.tfs);
					VotedGlobal g = new VotedGlobal(esStatistic.totaltf);
					Label label = Global.v().voter.vote(u, g);
					esStatistic.setLabel(klassName, methodName, es, label);

				}
			}
		}
		
		esStatistic.toSortedXML(new File(Global.v().toDoDir,Global.v().modelName+"ES.xml"));
		
		toBlock();
	}

	private void toBlock() {
		Options.v().set_keep_line_number(true);
		// Options.v().setPhaseOption("jb", "use-original-names:true");
		Scene.v().loadBasicClasses();
		
		for (String klassName : esStatistic.getKlassNames()) {
			for (String methodName : esStatistic.getMethodNames(klassName)) {
				HashMap<Integer, StatisticProperty> blkData = new HashMap<Integer, StatisticProperty>();
				
				for (String es : esStatistic.getEss(klassName, methodName)) {

					DefaultLabel ld = (DefaultLabel) (esStatistic.getProperty(
							klassName, methodName, es).label);


					SootClass sc = Scene.v().loadClassAndSupport(klassName);
					SootMethod sm = sc.getMethod(methodName);
					Body body = sm.retrieveActiveBody();
					BlockGraph bGraph = new ExceptionalBlockGraph(
							body);

					Set<Integer> blkSet = new HashSet<Integer>();
					String[] ess = es.split(">");
					int length;
					List<Block> blockLs = bGraph.getBlocks();
					length = ess.length;
					
					int startBlkIdx = Integer.parseInt(ess[0]);
//					int firstBlkIdx = Integer.parseInt(ess[1]);
////					if(startBlkIdx==-1)
////						getConnPreBlock(blockLs.get(firstBlkIdx), blockLs, blkSet);
////					else if(startBlkIdx==-2) {
////						getConnPreBlock(blockLs.get(firstBlkIdx), blockLs, blkSet);
//					if(startBlkIdx<0) {
//						getConnPreBlock(blockLs.get(firstBlkIdx), blockLs, blkSet);
//					}else
//						getConnPreBlock(blockLs.get(startBlkIdx), blockLs, blkSet);
					
					blkSet.add(startBlkIdx);
					
					for (int k = 1; k < length - 1; k++) {
						int nxk = Integer.parseInt(ess[k]);
						blkSet.add(nxk);
					}

					int endBlkIdx = Integer.parseInt(ess[length - 1]);
					
					endBlkIdx = endBlkIdx>=0 ? endBlkIdx : -endBlkIdx;
					
					getConnSucBlock(blockLs.get(endBlkIdx), blockLs, blkSet);
					
//					if (endBlkIdx == -1) {
//						Block b = blockLs
//								.get(Integer.parseInt(ess[length - 2]) + 1);
//						getConnSucBlock(b, blockLs, blkSet);
//					} else {
//						blkSet.add(endBlkIdx);
//						for (Block b : blockLs.get(endBlkIdx).getSuccs()) {
//							if (b.getHead() instanceof IdentityStmt) {
//								continue;
//							}
//							if (b.getIndexInMethod() != endBlkIdx + 1) {
//								getConnSucBlock(b, blockLs, blkSet);
//								break;
//							}
//						}
//					}
					for (Integer i : blkSet) {
						StatisticProperty sp = blkData.get(i);
						if (sp == null) {
							sp = new StatisticProperty();
						}
						if (sp.label == null) {
							DefaultLabel lwc = new DefaultLabel(
									ld.getGene(), ld.getMainGene());
							sp.label = lwc;
							sp.tfs.add(false);
						} else {
							DefaultLabel lwc = (DefaultLabel) sp.label;

							float xx = lwc.getMinGene()+ld.getMainGene();
							
							if (lwc.compareTo(ld)==-1) {
								lwc.copy(ld);
							}
							lwc.setMinGene(xx);
							sp.label = lwc;
							sp.tfs.add(false);
						}
						blkData.put(i, sp);
					}
				}
				for (Integer i : blkData.keySet()) {
					StatisticProperty sp = blkData.get(i);
					DefaultLabel lwc = (DefaultLabel) sp.label;
//					if(lwc.getTiesLayer()>3)
//						throw new IllegalStateException("ddd");
//					lwc.setMinGene(lwc.getMinGene() / sp.tfs.getFlsTimes());
					lwc.setMinGene(lwc.getMinGene());
				}

				blkStatistic.setStatisticProperty(klassName, methodName,
						blkData);
				
				
			}
		}
		blkStatistic.toSortedXML(new File(Global.v().toDoDir, Global.v().modelName+"blk.xml"));
		
	}

//	private void getConnPreBlock(Block b, List<Block> blockLs,
//			Set<Integer> blkSet) {
//		int blkIdx = b.getIndexInMethod();
//		boolean ret = false;
//		blkSet.add(blkIdx);
//		if(b.getPreds().size()!=1)
//			return;
//		else
//			getConnPreBlock(b.getPreds().get(0), blockLs, blkSet);
//	}
//
//	// all the pred and suc block is stop at which block's tail is IF or GOTO.
//	// we can also exclude GOTO which may be more accurate
//	// but for easy,we omit it
//
//	private void getConnSucBlock(Block b, List<Block> blockLs,
//			Set<Integer> blkSet) {
//		int blkIdx = b.getIndexInMethod();
//
//		blkSet.add(blkIdx);
//		
//		if(b.getSuccs().size()!=1)
//				return;
//		else
//			getConnSucBlock(b.getSuccs().get(0), blockLs, blkSet);
//			
//			
//	}
	
	
	private void getConnPreBlock(Block b, List<Block> blockLs,
			Set<Integer> blkSet) {
		int blkIdx = b.getIndexInMethod();
		boolean ret = false;
		Block block = b;

		while (!ret) {
			if (!block.getTail().branches()) {
//			if(!(block.getTail() instanceof IfStmt)) {
				blkSet.add(blkIdx);
				blkIdx--;
				if (blkIdx < 0)
					ret = true;
				else
					block = blockLs.get(blkIdx);
			} else
				ret = true;
		}
	}

	// all the pred and suc block is stop at which block's tail is IF or GOTO.
	// we can also exclude GOTO which may be more accurate
	// but for easy,we omit it

	private void getConnSucBlock(Block b, List<Block> blockLs,
			Set<Integer> blkSet) {
		int blkIdx = b.getIndexInMethod();
		boolean ret = false;
		Block block = b;
		while (!ret) {
			blkSet.add(blkIdx);
//			if (block.getTail().branches();
			if((block.getTail().branches())
					|| block.getTail() instanceof ReturnStmt
					|| block.getTail() instanceof ReturnVoidStmt) {
				ret = true;
			} else {
				blkIdx++;
				if (blkIdx >= blockLs.size())
					ret = true;
				else {
					block = blockLs.get(blkIdx);
				}
			}
		}
	}
	
}